/////////////////////////////////////////////////////////////////////////////
// Name:        interpreter.cpp
// Author:      Nicolay Giraldo
// Created:     15/12/05
// Copyright:   Nicolay Giraldo
/////////////////////////////////////////////////////////////////////////////

#ifdef __BORLANDC__
    #pragma hdrstop
#endif

#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#  include <wx/wx.h>
#endif

#include <wx/hashmap.h>
#include <wx/txtstrm.h>
#include <wx/sstream.h>
#include <wx/wfstream.h>
#include <wx/txtstrm.h>
#include <wx/splitter.h>
#include <wx/notebook.h>
#include <wx/spinbutt.h>
//#include <wx/richtext/richtextctrl.h>
#include <wx/textctrl.h>
#include <wx/log.h>
#include <wx/thread.h>
#include <wx/dynarray.h>
#include <wx/regex.h>
#include <wx/datetime.h>

#include <tommath.h>

#include "types.h"
#include "ide.h"
#include "interpreter.h"
#include "standardfunctions.h"


wxString 
reader::readToken(wxTextInputStream& tistream)
{
//	wxLogNull ln;
	wxString separators(_T(" \t\n\r\0"));
	wxString delimiters(_T("()'`,@#"));
	
	wxString result;
	wxChar rchar;
	
	keepreading:
	if (haschar)
	{
		rchar = cbuffer;
		haschar = false;
	}
	else
	{
		rchar = tistream.GetChar();
//		wxLogDebug(_T("readToken: readChar: %d : %c"), rchar, rchar);
	}
	
	if (!rchar)
	{
		return result;
	}
	
	if (rchar == _T(';'))
	{
//		wxLogDebug(_T("readToken: match comment delimiter: %c"), rchar);
		if (result.Length())
		{
			cbuffer = rchar;
			haschar = true;
			return result;
		}
		else 
		{
			do
			{
				rchar = tistream.GetChar();
//				wxLogDebug(_T("readToken: readChar: %d : %c"), rchar, rchar);
			}
			while (rchar != _T('\r') && rchar != _T('\n') && (rchar != 0));
			goto keepreading;
		}
	}
	
	if (rchar == _T('"'))
	{
//		wxLogDebug(_T("readToken: match string delimiter: %c"), rchar);
		if (result.Length())
		{
			cbuffer = rchar;
			haschar = true;
			return result;
		}
		else 
		{
			do
			{
				doread:
				result += rchar;
				rchar = tistream.GetChar();
//				wxLogDebug(_T("readToken: readChar: %d : %c"), rchar, rchar);
				if (rchar == _T('\0'))
				{
					wxLogError(_T("readToken: Input stream ends within a string"));
					result = _T("");
					return result;
				}
				if (rchar == _T('\\'))
				{
//					wxLogDebug(_T("readToken: match string scape char: %c"), rchar);
					rchar = tistream.GetChar();
//					wxLogDebug(_T("readToken: readChar: %d : %c"), rchar, rchar);
					goto doread;
				}
			}
			while (rchar != _T('"'));
			result += rchar;
			return result;
		}
	}
	
	if (delimiters.Find(rchar) != -1)
	{
//		wxLogDebug(_T("readToken: match delimiter: %c"), rchar);
		if (result.Length())
		{
			cbuffer = rchar;
			haschar = true;
			return result;
		}
		else 
		{
			if (rchar == _T(','))
			{
				rchar = tistream.GetChar();
//				wxLogDebug(_T("readToken: readChar: %d : %c"), rchar, rchar);
				if (rchar == _T('@'))
				{
					return wxString(_T(",@"));
				}
				else
				{
					cbuffer = rchar;
					haschar = true;
					return wxString(_T(","));
				}
			}
			result += rchar;
			return result;
		}
	}
	
	if (separators.Find(rchar) != -1)
	{
//		wxLogDebug(_T("readToken: match separator: %c"), rchar);
		if (result.Length())
		{
			haschar = false;
			return result;
		}
		else 
		{
			goto keepreading;
		}
	}
	
	result += rchar;
	goto keepreading;
//	return tistream.ReadWord();;
}

value* 
reader::parseToken(wxString token)
{
//	wxLogStderr stder;
	if (token.length())
	{
//		wxString rest;
		if (token[0] == _T('\"'))
		{
//			wxLogDebug(_T("parseToken: %s as string"), token.c_str());
			return new string(token.Remove(0, 1).RemoveLast());
		}
		if (token.IsSameAs(_("nil"), false))
		{
//			wxLogDebug(_T("parseToken: %s as nil"), token.c_str());
			return NULL;
		}
		wxRegEx numregexp;
		numregexp.Compile(_T("^([\\+\\-])?([0-9]*)\\.([0-9]+)([deflsDEFLS][\\+\\-]?[0-9]+)?$"), wxRE_ADVANCED);
		if (numregexp.Matches(token))
		{
			//TODO: make the parsing in a loop without using ToDouble .. ?
			wxString exponent = numregexp.GetMatch(token, 4);
			exponent.LowerCase();
			if (exponent.Length())
			{
				wxChar exponent_marker = exponent[0];
				exponent.Replace(_T("d"), _T("e"));
				exponent.Replace(_T("f"), _T("e"));
				exponent.Replace(_T("l"), _T("e"));
				exponent.Replace(_T("s"), _T("e"));
				wxString replacement;
				replacement.Printf(_T("\\1\\2.\\3%s"), exponent.c_str());
				numregexp.Replace(&token, replacement);
				if (exponent_marker == _T('f') || exponent_marker == _T('s'))
				{
					singlefloat* result = new singlefloat();
					double tempd;
					if (!token.ToDouble(&tempd))
					{
						wxLogError(_T("Floating point overflow"));
					}
					result->num = tempd;
//					wxLogDebug(_T("parseToken: %s as singlefloat"), token.c_str());
					return result;
				}
			}
			doublefloat* result = new doublefloat();
			token.ToDouble(&(result->num));
//			wxLogDebug(_T("parseToken: %s as doublefloat"), token.c_str());
			return result;
		}
		numregexp.Compile(_T("^([\\+\\-])?([0-9]+)(\\.[0-9]*)?([deflsDEFLS][\\+\\-]?[0-9]+)$"), wxRE_ADVANCED);
		if (numregexp.Matches(token))
		{
			//TODO: make the parsing in a loop without using ToDouble .. ?
			wxString exponent = numregexp.GetMatch(token, 4);
			exponent.LowerCase();
			if (exponent.Length())
			{
				wxChar exponent_marker = exponent[0];
				exponent.Replace(_T("d"), _T("e"));
				exponent.Replace(_T("f"), _T("e"));
				exponent.Replace(_T("l"), _T("e"));
				exponent.Replace(_T("s"), _T("e"));
				wxString replacement;
				replacement.Printf(_T("\\1\\2\\3%s"), exponent.c_str());
				numregexp.Replace(&token, replacement);
				if (exponent_marker == _T('f') || exponent_marker == _T('s'))
				{
					singlefloat* result = new singlefloat();
					double tempd;
					if (!token.ToDouble(&tempd))
					{
						wxLogError(_T("Floating point overflow"));
					}
					result->num = tempd;
//					wxLogDebug(_T("parseToken: %s as doublefloat"), token.c_str());
					return result;
				}
			}
			doublefloat* result = new doublefloat();
			token.ToDouble(&(result->num));
//			wxLogDebug(_T("parseToken: %s as doublefloat"), token.c_str());
			return result;
		}
		numregexp.Compile(_T("^([\\+\\-]?)([0-9]+)(\\.)$"), wxRE_ADVANCED);
		if (numregexp.Matches(token))
		{
			wxString sign = numregexp.GetMatch(token, 1);
			wxString digits = numregexp.GetMatch(token, 2);
			long int oldtempl, templ = 0;
			bool overflow = false;
			for (unsigned int i = 0; i < digits.Length(); i++)
			{
				int digit = digits[i] - _T('0');
				oldtempl = templ;
				templ *= 10;
				if (templ / 10 != oldtempl)
				{
					overflow = true;
					break;
				}
				oldtempl = templ;
				templ += digit;
				if (templ - digit != oldtempl)
				{
					overflow = true;
					break;
				}
			}
			if (!overflow)
			{
				if (sign.IsSameAs(_T("-"))) templ *= -1;
				fixnum* result = new fixnum(templ);
//				wxLogDebug(_T("parseToken: %s as fixnum"), token.c_str());
				return result;
			}
			else
			{
				bignum* result = new bignum();
				result->read(token, 10);
//				wxLogDebug(_T("parseToken: %s as bignum"), token.c_str());
				return result;
			}
		}
		//TODO: use variable *READ-BASE* in the current environment to select valid digits in the following regexp
		numregexp.Compile(_T("^([\\+\\-]?)([0-9]+)$"), wxRE_ADVANCED);
		if (numregexp.Matches(token))
		{
			wxString sign = numregexp.GetMatch(token, 1);
			wxString digits = numregexp.GetMatch(token, 2);
			long int oldtempl, templ = 0;
			bool overflow = false;
			for (unsigned int i = 0; i < digits.Length(); i++)
			{
				int digit = digits[i] - _T('0'); //TODO: use mp_sw_rmap
				oldtempl = templ;
				templ *= 10; //TODO: use *read-base* instead of 10
				if (templ / 10 != oldtempl)
				{
					overflow = true;
					break;
				}
				oldtempl = templ;
				templ += digit;
				if (templ - digit != oldtempl)
				{
					overflow = true;
					break;
				}
			}
			if (!overflow)
			{
				if (sign.IsSameAs(_T("-"))) templ *= -1;
				fixnum* result = new fixnum(templ);
//				wxLogDebug(_T("parseToken: %s as fixnum"), token.c_str());
				return result;
			}
			else
			{
				bignum* result = new bignum();
				result->read(token, 10); //TODO: use *read-base* instead of 10
//				wxLogDebug(_T("parseToken: %s as bignum"), token.c_str());
				return result;
			}
		}
		//TODO: use variable *READ-BASE* in the current environment to select valid digits in the following regexp
		numregexp.Compile(_T("^([\\+\\-]?)([0-9]+)/([0-9]+)$"), wxRE_ADVANCED);
		if (numregexp.Matches(token))
		{
			wxString sign = numregexp.GetMatch(token, 1);
			wxString digits = numregexp.GetMatch(token, 2);
			long int oldtempl, templ = 0;
			bool overflow = false;
			unsigned int i;
			for (i = 0; i < digits.Length(); i++)
			{
				int digit = digits[i] - _T('0'); //TODO: use mp_sw_rmap
				oldtempl = templ;
				templ *= 10; //TODO: use *read-base* instead of 10
				if (templ / 10 != oldtempl)
				{
					overflow = true;
					break;
				}
				oldtempl = templ;
				templ += digit;
				if (templ - digit != oldtempl)
				{
					overflow = true;
					break;
				}
			}
			integer* numerator;
			if (!overflow)
			{
				if (sign.IsSameAs(_T("-"))) templ *= -1;
				numerator = new fixnum(templ);
//				wxLogDebug(_T("parseToken: %s as fixnum"), token.c_str());
//				return result;
			}
			else
			{
				numerator = new bignum();
				((bignum*)numerator)->read(digits, 10); //TODO: use *read-base* instead of 10
				if (sign.IsSameAs(_T("-"))) ((bignum*)numerator)->num.sign = MP_NEG;
//				wxLogDebug(_T("parseToken: %s as bignum"), token.c_str());
//				return result;
			}
			digits = numregexp.GetMatch(token, 3);
			oldtempl = templ = 0;
			overflow = false;
			for (i = 0; i < digits.Length(); i++)
			{
				int digit = digits[i] - _T('0'); //TODO: use mp_sw_rmap
				oldtempl = templ;
				templ *= 10; //TODO: use *read-base* instead of 10
				if (templ / 10 != oldtempl)
				{
					overflow = true;
					break;
				}
				oldtempl = templ;
				templ += digit;
				if (templ - digit != oldtempl)
				{
					overflow = true;
					break;
				}
			}
			integer* denominator;
			if (!overflow)
			{
				denominator = new fixnum(templ);
//				wxLogDebug(_T("parseToken: %s as fixnum"), token.c_str());
//				return result;
			}
			else
			{
				denominator = new bignum();
				((bignum*)denominator)->read(digits, 10); //TODO: use *read-base* instead of 10
//				wxLogDebug(_T("parseToken: %s as bignum"), token.c_str());
//				return result;
			}
			return &((new ratio(*numerator, *denominator))->chkdenom());
		}
//		wxLogDebug(_T("parseToken: %s as symbol"), token.c_str());
		if (token[0] == _T(':'))
		{
			wxString* newstring = new wxString(token.AfterFirst(_T(':')));
			return new symbol(*newstring, true);
		}
		wxString* newstring = new wxString(token);
		return new symbol(*newstring);
	}
	else
	{
//		wxLogDebug(_T("parseToken: %s as nil"), token.c_str());
		return NULL;
	}
}

//int reader::parens;
//bool reader::rootset;
bool reader::haschar = false;
wxChar reader::cbuffer;


value* 
reader::read(wxInputStream& istream)
{
//	wxLogStderr stder;
	value* result = NULL;
	value* resultroot = NULL;
	wxTextInputStream tistream(istream);
//	tistream.SetStringSeparators(_T(" \t\r\n"));
	
	wxString token;
	wxString rest;
	
	keepreading:
	
	token = readToken(tistream);
	
	
	if (parens)
	{
		if (token.IsSameAs(_T("")))
		{
			wxLogError(_T("read: Stream finished in the middle of an s-expression"));
			return NULL;
		}
		else if (token.IsSameAs(_T("'")))
		{
//			wxLogDebug(_T("read: quote inside parens"));
			cons* ncons3 = new cons();
			cons* ncons2 = new cons(new symbol(_T("quote")), ncons3);
			cons* ncons1 = new cons(ncons2, NULL);
			reader newreader = reader(0, backquote);
			ncons3->car = newreader.read(istream);
			if (result)
			{
				((cons*)result)->cdr = ncons1;
			}
			result = ncons1;
			if (!rootset)
			{
				resultroot = result;
				rootset = true;
			}
			result = ncons1;
			goto keepreading;
		}
		if (token.IsSameAs(_T("`")))
		{
//			wxLogDebug(_T("read: backquote inside parens"));
			cons* ncons3 = new cons();
			cons* ncons2 = new cons(new symbol(_T("quasiquote")), ncons3);
			cons* ncons1 = new cons(ncons2, NULL);
			reader newreader = reader(0, backquote+1);
			ncons3->car = newreader.read(istream);
			if (result)
			{
				((cons*)result)->cdr = ncons1;
			}
			result = ncons1;
			if (!rootset)
			{
				resultroot = result;
				rootset = true;
			}
			result = ncons1;
			goto keepreading;
		}
		if (token.IsSameAs(_T(",")))
		{
//			wxLogDebug(_T("read: comma inside parens"));
			if (backquote)
			{
				cons* ncons3 = new cons();
				cons* ncons2 = new cons(new symbol(_T("unquote")), ncons3);
				cons* ncons1 = new cons(ncons2, NULL);
				reader newreader = reader(0, backquote-1);
				ncons3->car = newreader.read(istream);
				if (result)
				{
					((cons*)result)->cdr = ncons1;
				}
				result = ncons1;
				if (!rootset)
				{
					resultroot = result;
					rootset = true;
				}
				result = ncons1;
				goto keepreading;
			}
			else
			{
				wxLogError(_T("read: comma can't be used outside a backquote"));
				return NULL;
//				goto keepreading;
			}
		}
		if (token.IsSameAs(_T(",@")))
		{
//			wxLogDebug(_T("read: comma and at inside parens"));
			if (backquote)
			{
				cons* ncons3 = new cons();
				cons* ncons2 = new cons(new symbol(_T("unquote-splicing")), ncons3);
				cons* ncons1 = new cons(ncons2, NULL);
				reader newreader = reader(0, backquote-1);
				ncons3->car = newreader.read(istream);
				if (result)
				{
					((cons*)result)->cdr = ncons1;
				}
				result = ncons1;
				if (!rootset)
				{
					resultroot = result;
					rootset = true;
				}
				result = ncons1;
				goto keepreading;
			}
			else
			{
				wxLogError(_T("read: comma and at can't be used outside a backquote"));
				return NULL;
//				goto keepreading;
			}
		}
		else if (token.IsSameAs(_T("(")))
		{
//			wxLogDebug(_T("read: ( inside parens"));
			cons* ncons = new cons();
			reader newreader = reader(parens+1, backquote);
			ncons->car = newreader.read(istream);
			if (result)
			{
				((cons*)result)->cdr = ncons;
			}
			
			result = ncons;
			
			if (!rootset)
			{
				resultroot = ncons;
				rootset = true;
			}
			
			goto keepreading;
		}
		else if (token.IsSameAs(_T(".")))
		{
//			wxLogDebug(_T("read: . inside parens"));
			if (result)
			{
				reader newreader = reader(0, backquote);
				((cons*)result)->cdr = newreader.read(istream);
				token = readToken(tistream);
				if (token.IsSameAs(_T(")")))
				{
//					wxLogDebug(_T("read: returning from read, end point list, parens: %d"), parens);
					return resultroot;
				}
				else
				{
					//raise error!
					wxLogError(_T("read: Illegal end of dotted list"));
					return NULL;
//					goto keepreading;
				}
			}
			else 
			{
				//raise error!
				wxLogError(_T("read: Token \".\" not allowed here"));
				return NULL;
//				goto keepreading;
			}
//			goto keepreading; //unreacheable code
		}
		else if (token.IsSameAs(_T(")")))
		{
//			wxLogDebug(_T("read: )"));
//			wxLogDebug(_T("read: returning from read, end list, parens: %d"), parens);
			if (!resultroot)
			{
				return NULL;
			}
			return resultroot;
		}
		else
		{
//			wxLogDebug(_T("read: parens level %d, token read"), parens);
			cons* ncons = new cons(parseToken(token), NULL);
			if (result)
			{
				((cons*)result)->cdr = ncons;
			}
			
			result = ncons;
			
			if (!rootset)
			{
				resultroot = ncons;
				rootset = true;
			}
			
			goto keepreading;
		}
	}
	else //not parens
	{
		if (token.IsSameAs(_T("")))
		{
//			wxLogDebug(_T("read: returning from read, no token"));
			return resultroot;
		}
		else if (token.IsSameAs(_T("'")))
		{
//			wxLogDebug(_T("read: quote"));
			cons* ncons2 = new cons();
			cons* ncons1 = new cons(new symbol(_T("quote")), ncons2);
			reader newreader = reader(0, backquote);
			ncons2->car = newreader.read(istream);
			result = ncons1;
			if (!rootset)
			{
				resultroot = result;
				rootset = true;
			}
			result = ncons2;
//			wxLogDebug(_T("read: returning from read, quote"));
			return resultroot;
		}
		if (token.IsSameAs(_T("`")))
		{
//			wxLogDebug(_T("read: backquote"));
			cons* ncons2 = new cons();
			cons* ncons1 = new cons(new symbol(_T("quasiquote")), ncons2);
			reader newreader = reader(0,backquote+1);
			ncons2->car = newreader.read(istream);
			result = ncons1;
			if (!rootset)
			{
				resultroot = result;
				rootset = true;
			}
			result = ncons2;
//			wxLogDebug(_T("read: returning from read, backquote"));
			return resultroot;
		}
		if (token.IsSameAs(_T(",")))
		{
			if (backquote)
			{
//				wxLogDebug(_T("read: comma"));
				cons* ncons2 = new cons();
				cons* ncons1 = new cons(new symbol(_T("unquote")), ncons2);
				reader newreader = reader(0,backquote-1);
				ncons2->car = newreader.read(istream);
				result = ncons1;
				if (!rootset)
				{
					resultroot = result;
					rootset = true;
				}
				result = ncons2;
//				wxLogDebug(_T("read: returning from read, unquote"));
				return resultroot;
			}
			else
			{
				wxLogError(_T("read: comma can't be used outside a backquote"));
				return NULL;
//				goto keepreading;
			}
		}
		if (token.IsSameAs(_T(",@")))
		{
//			wxLogDebug(_T("read: comma and at"));
			if (backquote)
			{
				cons* ncons2 = new cons();
				cons* ncons1 = new cons(new symbol(_T("unquote-splicing")), ncons2);
				reader newreader = reader(0,backquote-1);
				ncons2->car = newreader.read(istream);
				result = ncons1;
				if (!rootset)
				{
					resultroot = result;
					rootset = true;
				}
				result = ncons2;
//				wxLogDebug(_T("read: returning from read, unquote-splicing"));
				return resultroot;
			}
			else
			{
				wxLogError(_T("read: ,@ can't be used outside a backquote"));
				return NULL;
//				goto keepreading;
			}
		}
		else if (token.IsSameAs(_T("(")))
		{
//			wxLogDebug(_T("("));
			reader newreader = reader(parens+1, backquote);
			result = newreader.read(istream);
			if (!rootset)
			{
				resultroot = result;
				rootset = true;
			}
//			wxLogDebug(_T("read: returning from read, list"));
			return resultroot;
		}
		else if (token.IsSameAs(_T(")")))
		{
			wxLogError(_T("read: An object can't start with )"));
//			wxLogDebug(_T("read: )"));
//			wxLogDebug(_T("read: returning from read, error"));
			return NULL;
		}
		else if (token.IsSameAs(_T(".")))
		{
			//TODO:  raise error!
			wxLogError(_T("read: Token \".\" not allowed here"));
			return NULL;
//			goto keepreading;
		}
		else
		{
//			wxLogDebug(_T("read: else, token parsed"));
			result = reader::parseToken(token);
			if (!rootset)
			{
				resultroot = result;
				rootset = true;
			}
//			wxLogDebug(_T("read: returning from read, token"));
			return resultroot;
		}
	}
	
//	wxLogDebug(_T("read: returning from read, bottom of function"));
//	return resultroot;  // unreachable code
}


void 
print(value* expr, wxOutputStream& ostream)
{
	wxTextOutputStream tostream(ostream);
	if (expr != NULL)
	{
		switch (expr->getType())
		{
		// (consp expr)
		case t_cons:
			{
				cons* dacons = (cons*)expr;
				if (car(dacons) != NULL && car(dacons)->getType() == t_symbol)
				{
					wxString sname = ((symbol*)car(dacons))->name;
					if (sname.IsSameAs(_T("quote"), false) && cdr(dacons) != NULL && cdr(dacons)->getType() == t_cons)
					{
						tostream << _T('\'');
						print(car(cdr(dacons)), ostream);
						return;
					}
					if (sname.IsSameAs(_T("quasiquote"), false) && cdr(dacons) != NULL && cdr(dacons)->getType() == t_cons)
					{
						tostream << _T('`');
						print(car(cdr(dacons)), ostream);
						return;
					}
					if (sname.IsSameAs(_T("unquote"), false) && cdr(dacons) != NULL && cdr(dacons)->getType() == t_cons)
					{
						tostream << _T(',');
						print(car(cdr(dacons)), ostream);
						return;
					}
					if (sname.IsSameAs(_T("unquote-splicing"), false) && cdr(dacons) != NULL && cdr(dacons)->getType() == t_cons)
					{
						tostream << _T(',') << _T('@');
						print(car(cdr(dacons)), ostream);
						return;
					}
				}
				tostream << _T('(');
			step2:
				print(car(dacons), ostream);
				value* acdr = cdr(dacons);
				if (acdr != NULL)
				{
					if (acdr->getType() == t_cons)
					{
						dacons = (cons*)acdr;
						tostream << _T(' ');
						goto step2;
					}
					tostream << wxString(_(" . "));
					print(acdr, ostream);
				}
				tostream << _T(')');
				break;
			}
		case t_keyword:
			// prints the name of the symbol.
			tostream << _T(':');
			// TODO: case and escaping stuff?
			tostream << ((symbol*)expr)->name;
			break;
		case t_symbol:
			// prints the name of the symbol.
			// TODO: case and escaping stuff?
			tostream << ((symbol*)expr)->name;
			break;
		case t_string:
			tostream << _T('"');
			{
				wxString rstring(((string*)expr)->contents);
				rstring.Replace(_T("\\"), _T("\\\\"));
				rstring.Replace(_T("\""), _T("\\\""));
				rstring.Replace(_T("\r\n"), _T("\n"));
				tostream << rstring;
			}
			tostream << _T('"');
			break;
		case t_singlefloat:
			tostream << ((singlefloat*)expr)->num;
			break;
		case t_doublefloat :
			tostream << ((doublefloat*)expr)->num;
			break;
		case t_fixnum    :
			// TODO: use *read-base* instead of 10
			tostream << ((fixnum*)expr)->num;
			break;
		case t_bignum    :
			{
				// TODO: use *read-base* instead of 10
				wxString biggie;
				((bignum*)expr)->write(biggie, 10);
				tostream << biggie;
			}
			break;
		case t_ratio     :
			print((((ratio*)expr)->numerator), ostream);
			tostream << _T('/');
			print((((ratio*)expr)->denominator), ostream);
			break;
		default:
		// other values ... ?.
			break;
		}
	}
	else
	{
		// NULL is nil, so it evaluates to itself
		tostream << wxString(_("nil"));
	}
}


void 
guiprint(value* expr, wxTextCtrl& otxtctrl)
{
//	wxTextOutputStream tostream(ostream);
	if (expr != NULL)
	{
		switch (expr->getType())
		{
		// (consp expr)
		case t_cons:
			{
				cons* dacons = (cons*)expr;
				if (dacons->car != NULL && dacons->car->getType() == t_symbol)
				{
					wxString sname = ((symbol*)dacons->car)->name;
					if (sname.IsSameAs(_T("quote"), false) && cdr(dacons) != NULL && cdr(dacons)->getType() == t_cons)
					{
						otxtctrl.SetDefaultStyle(delimitersstyle);
						otxtctrl << _T('\'');
						otxtctrl.SetDefaultStyle(defaultstyle   );
						guiprint(car(cdr(dacons)), otxtctrl);
						return;
					}
					if (sname.IsSameAs(_T("quasiquote"), false) && cdr(dacons) != NULL && cdr(dacons)->getType() == t_cons)
					{
						otxtctrl.SetDefaultStyle(delimitersstyle);
						otxtctrl << _T('`');
						otxtctrl.SetDefaultStyle(defaultstyle   );
						guiprint(car(cdr(dacons)), otxtctrl);
						return;
					}
					if (sname.IsSameAs(_T("unquote"), false) && cdr(dacons) != NULL && cdr(dacons)->getType() == t_cons)
					{
						otxtctrl.SetDefaultStyle(delimitersstyle);
						otxtctrl << _T(',');
						otxtctrl.SetDefaultStyle(defaultstyle   );
						guiprint(car(cdr(dacons)), otxtctrl);
						return;
					}
					if (sname.IsSameAs(_T("unquote-splicing"), false) && cdr(dacons) != NULL && cdr(dacons)->getType() == t_cons)
					{
						otxtctrl.SetDefaultStyle(delimitersstyle);
						otxtctrl << _T(',') << _T('@');
						otxtctrl.SetDefaultStyle(defaultstyle   );
						guiprint(car(cdr(dacons)), otxtctrl);
						return;
					}
				}
				otxtctrl.SetDefaultStyle(delimitersstyle);
				otxtctrl << _T('(');
				otxtctrl.SetDefaultStyle(defaultstyle   );
			step2:
				guiprint(car(dacons), otxtctrl);
				value* acdr = cdr(dacons);
				if (acdr != NULL)
				{
					if (acdr->getType() == t_cons)
					{
						dacons = (cons*)acdr;
						otxtctrl << _T(' ');
						goto step2;
					}
					otxtctrl.SetDefaultStyle(delimitersstyle);
					otxtctrl << wxString(_(" . "));
					otxtctrl.SetDefaultStyle(defaultstyle   );
					guiprint(acdr, otxtctrl);
				}
				otxtctrl.SetDefaultStyle(delimitersstyle);
				otxtctrl << _T(')');
				otxtctrl.SetDefaultStyle(defaultstyle   );
				break;
			}
		case t_keyword:
			// prints the name of the keyword.
			// TODO: case and escaping stuff?
			otxtctrl.SetDefaultStyle(delimitersstyle);
			otxtctrl << _T(':');
			otxtctrl.SetDefaultStyle(keywordstyle   );
			otxtctrl << ((symbol*)expr)->name;
			otxtctrl.SetDefaultStyle(defaultstyle   );
			break;
		case t_symbol:
			// prints the name of the symbol.
			// TODO: case and escaping stuff?
			otxtctrl.SetDefaultStyle(symbolsstyle   );
			otxtctrl << ((symbol*)expr)->name;
			otxtctrl.SetDefaultStyle(defaultstyle   );
			break;
		case t_string:
			otxtctrl.SetDefaultStyle(stringsstyle   );
			otxtctrl << _T('"');
			{
				wxString rstring(((string*)expr)->contents);
				rstring.Replace(_T("\\"), _T("\\\\"));
				rstring.Replace(_T("\""), _T("\\\""));
				rstring.Replace(_T("\r\n"), _T("\n"));
				otxtctrl << rstring;
			}
			otxtctrl << _T('"');
			otxtctrl.SetDefaultStyle(defaultstyle   );
			break;
		case t_singlefloat:
			otxtctrl.SetDefaultStyle(floatsstyle    );
			otxtctrl << ((singlefloat*)expr)->num;
			otxtctrl.SetDefaultStyle(defaultstyle   );
			break;
		case t_doublefloat :
			otxtctrl.SetDefaultStyle(floatsstyle    );
			otxtctrl << ((doublefloat*)expr)->num;
			otxtctrl.SetDefaultStyle(defaultstyle   );
			break;
		case t_fixnum    :
			// TODO: use *read-base* instead of 10
			otxtctrl.SetDefaultStyle(fixnumsstyle   );
			otxtctrl << ((fixnum*)expr)->num;
			otxtctrl.SetDefaultStyle(defaultstyle   );
			break;
		case t_bignum    :
			{
				// TODO: use *read-base* instead of 10
				wxString biggie;
				((bignum*)expr)->write(biggie, 10);
				otxtctrl.SetDefaultStyle(bignumsstyle   );
				otxtctrl << biggie;
				otxtctrl.SetDefaultStyle(defaultstyle   );
			}
			break;
		case t_ratio     :
			guiprint((((ratio*)expr)->numerator), otxtctrl);
			otxtctrl.SetDefaultStyle(delimitersstyle);
			otxtctrl << _T('/');
			otxtctrl.SetDefaultStyle(defaultstyle   );
			guiprint((((ratio*)expr)->denominator), otxtctrl);
			break;
		default:
		// other values ... ?.
			break;
		}
	}
	else
	{
		// NULL is nil, so it evaluates to itself
		otxtctrl.SetDefaultStyle(symbolsstyle   );
		otxtctrl << wxString(_("nil"));
		otxtctrl.SetDefaultStyle(defaultstyle   );
	}
}


void
interpreter::eval(evalframe* eframe)
{
	// The way to program this custom-stack-frames evaluator correctly is avoiding the C++ stack frame. 
	// This is achieved by prohibiting calling the eval function itself.
	// Instead of that, we use labels, and of course, eval frames.
	start:
	if (eframe)
	{
		if (eframe->isspecialform)
		{
			switch (eframe->specialform)
			{
			case sf_quote:
				if (eframe->n == 1)
				{
					eframe->result = eframe->pargs[0];
					eframe->nresults = 1;
					goto pop;
				}
				wxLogError(_T("Invalid number of arguments to special operator quote, called with %d arguments"), eframe->n);
				eframe->nresults = 0;
				goto pop;
//				break;
			case sf_if:
				if (eframe->i == 2)
				{
					eframe->result = eframe->eargs[1];
					goto pop;
				}
				if (eframe->i == 1)
				{
					evalframe* neframe = new evalframe(eframe);
					if (eframe->eargs[0]) // this is the real if of the evaluator !
					{
						neframe->expr = eframe->pargs[1];
					}
					else if (eframe->n == 3)
					{
						neframe->expr = eframe->pargs[2];
					}
					else
					{
						eframe->result = NULL;
						wxDELETE(neframe);
						goto pop;
					}
					eframe = neframe;
					goto start;
				}
				if (eframe->n == 2 || eframe->n == 3)
				{
					evalframe* neframe;
					neframe = new evalframe(eframe);
					neframe->expr = eframe->pargs[0];
					eframe->nresults = 1;
					eframe = neframe;
					goto start;
				}
				wxLogError(_T("Invalid number of arguments to special operator if, called with %d arguments"), eframe->n);
				eframe->nresults = 0;
				goto pop;
//				break;
			case sf_progn:
				if (eframe->n > eframe->i)
				{
					evalframe* neframe;
					neframe = new evalframe(eframe);
					neframe->expr = eframe->pargs[eframe->i];
					eframe = neframe;
					goto start;
				}
				else
				{
					if (eframe->i)
					{
						eframe->result = eframe->eargs[eframe->i-1];
					}
					goto pop;
				}
			case sf_setq:
				if (eframe->n % 2 == 0)
				{
					lb_setq:
					if (eframe->n > eframe->i)
					{
						if (eframe->i % 2 == 0)
						{
							eframe->eargs.Add(eframe->pargs[eframe->i]);
							eframe->i++;
							goto lb_setq;
						}
						else
						{
							evalframe* neframe;
							neframe = new evalframe(eframe);
							neframe->expr = eframe->pargs[eframe->i];
							eframe = neframe;
							goto start;
						}
					}
					else if (eframe->n == eframe->i)
					{
						wxMutexLocker(ngLispRuntime->globalmutex);
//						wxLogDebug(_T("Setq evaluation"));
						eframe->result = eframe->eargs[eframe->i - 1];
						for (unsigned int i = 0; i < eframe->i ; i+=2 )
						{
							if ((chk_nil(eframe->eargs[i]))->getType() != t_symbol)
							{
								wxLogError(_T("Argument %s for setq is not a symbol"), (printstring(eframe->eargs[i])).c_str());
								goto pop;
							}
							symbol* sim = (symbol*)chk_nil(eframe->eargs[i]);
							ngLispRuntime->globalenv.vmap[sim->name] = eframe->eargs[i+1];
						}
						goto pop;
					}
				}
				else
				{
					wxLogError(_T("Odd number of arguments to special form setq"));
					eframe->nresults = 0;
					goto pop;
				}
				break;
			default:
				wxLogError(_T("Special operator %s not implemented yet...  :("), ((symbol*)(eframe->fslot))->name.c_str());
				goto pop;
			}
		}
		if (eframe->isfunction)
		{
			switch (eframe->mfunction->ftype)
			{
			case f_invalid:
				wxLogError(_T("There is no function with name %s"), ((symbol*)(car(eframe->expr)))->name.c_str());
				goto pop;
			case f_systemfunction:
				{
					if (eframe->n > eframe->i)
					{
						evalframe* neframe;
						neframe = new evalframe(eframe);
						neframe->expr = eframe->pargs[eframe->i];
						eframe = neframe;
						goto start;
					}
					else if (eframe->n == eframe->i)
					{
						if (eframe->n > eframe->mfunction->numparams && !eframe->mfunction->has_rest_args)
						{
							wxLogError(_T("Too many parameters for function \"%s\""), ((symbol*)(car(eframe->expr)))->name.c_str());
							goto pop;
						}
						for (unsigned int i = 0; i < eframe->mfunction->numparams; i++)
						{
							eframe->mfunction->paramvalues.Add(NULL);
							switch (eframe->mfunction->paramspecifiers[i]->paramspec)
							{
							case p_required:
//								wxLogError(_T("Params passed : %d"), eframe->n);
								if (eframe->n < i+1)
								{
									wxLogError(_T("Too few parameters for function \"%s\""), ((symbol*)(car(eframe->expr)))->name.c_str());
									goto pop;
								}
								if (!chk_type(eframe->eargs[i], eframe->mfunction->paramspecifiers[i]->ptype))
								{
									wxLogError(_T("Type error in parameter %d: %s for function \"%s\""), i+1, (printstring(eframe->eargs[i])).c_str(), ((symbol*)(car(eframe->expr)))->name.c_str());
									goto pop;
								}
								eframe->mfunction->paramvalues[i] = eframe->eargs[i];
								break;
							case p_optional:
								if (eframe->n < i+1)  // not supplied
								{
									// TODO: delete this call to eval, and replace it with a new stack frame? (currently not used, but...)
//									eframe->mfunction->paramvalues[i] = eval(eframe->mfunction->paramspecifiers[i]->init_form, NULL);
									if (eframe->mfunction->paramspecifiers[i+1]->paramspec == p_supplied)
										eframe->mfunction->paramvalues[i+1] = trueval();
								}
								else
								{
									if (eframe->mfunction->paramspecifiers[i+1]->paramspec == p_supplied)
										eframe->mfunction->paramvalues[i+1] = NULL;
								}
								break;
							case p_supplied:
								// handled in the p_optional case
								break;
							case p_rest: case p_body:
								// TODO
								if (eframe->n < i+1) // not supplied
								{
									eframe->mfunction->paramvalues[i] = NULL;
								}
								else // supplied
								{
									cons* root = NULL;
									cons* current = NULL;
									for (unsigned int j = i; j < eframe->n; j++)
									{
										cons* temp = new cons();
										if (!root) current = root = temp;
										temp->car = eframe->eargs[j];
										if (current != temp) current->cdr = temp;
										current = temp;
									}
									eframe->mfunction->paramvalues[i] = root;
//									wxLogError(_T("Rest parameter: %s"), (printstring(root)).c_str());
								}
								break;
							case p_keyword:
								// TODO
								break;
							case p_symbol: //an alias for a keyword
								// TODO
								break;
							case p_aux:
								// TODO
								break;
							}
						}
//						wxLogError(_T("Evaluating function %s"), (printstring(eframe->expr)).c_str());
						eframe->result = eframe->mfunction->CallSystemFun();
						goto pop;
					}
				}
			}
		}
		if (eframe->expr)
		{
			switch (eframe->expr->getType())
			{
			// (consp expr)
			case t_cons:
				{
					// fill arg list
					if (!eframe->arglistfilled)
					{
						eframe->fslot = car(eframe->expr);
						value* nparam = cdr(eframe->expr);
						for (int i = 0; nparam; eframe->n++, i++)
						{
							if (nparam->getType() == t_cons)
							{
								eframe->pargs.Add(car(nparam));
								nparam = cdr(nparam);
							}
							else
							{
								wxLogError(_T("%s is not a list"), (printstring(nparam)).c_str());
								nparam = NULL;
								goto pop;
							}
						}
						eframe->arglistfilled = true;
					}
					if (eframe->fslot)
					{
						if (eframe->fslot->getType() == t_symbol)
						{
							wxString fname = ((symbol*)(eframe->fslot))->name;
							if (fname.IsSameAs(_T("block"), false))
							{
								eframe->isspecialform = true;
								eframe->specialform = sf_block;
								goto start;
							}
							// This three come from the paper "A Better API for First-Class Continuations"
							// by Marc Feeley
							if (fname.IsSameAs(_T("continuation-capture"), false))
							{
								eframe->isspecialform = true;
								eframe->specialform = sf_cont_capture;
								goto start;
							}
							if (fname.IsSameAs(_T("continuation-graft"), false))
							{
								eframe->isspecialform = true;
								eframe->specialform = sf_cont_graft;
								goto start;
							}
							if (fname.IsSameAs(_T("continuation-return"), false))
							{
								eframe->isspecialform = true;
								eframe->specialform = sf_cont_return;
								goto start;
							}
							if (fname.IsSameAs(_T("catch"), false))
							{
								eframe->isspecialform = true;
								eframe->specialform = sf_catch;
								goto start;
							}
							if (fname.IsSameAs(_T("declare"), false))
							{
								eframe->isspecialform = true;
								eframe->specialform = sf_declare;
								goto start;
							}
							if (fname.IsSameAs(_T("eval-when"), false))
							{
								eframe->isspecialform = true;
								eframe->specialform = sf_eval_when;
								goto start;
							}
							if (fname.IsSameAs(_T("flet"), false))
							{
								eframe->isspecialform = true;
								eframe->specialform = sf_flet;
								goto start;
							}
							if (fname.IsSameAs(_T("function"), false))
							{
								eframe->isspecialform = true;
								eframe->specialform = sf_function;
								goto start;
							}
							if (fname.IsSameAs(_T("go"), false))
							{
								eframe->isspecialform = true;
								eframe->specialform = sf_go;
								goto start;
							}
							if (fname.IsSameAs(_T("if"), false))
							{
								eframe->isspecialform = true;
								eframe->specialform = sf_if;
								goto start;
							}
							if (fname.IsSameAs(_T("labels"), false))
							{
								eframe->isspecialform = true;
								eframe->specialform = sf_labels;
								goto start;
							}
							if (fname.IsSameAs(_T("let"), false))
							{
								eframe->isspecialform = true;
								eframe->specialform = sf_let;
								goto start;
							}
							if (fname.IsSameAs(_T("let*"), false))
							{
								eframe->isspecialform = true;
								eframe->specialform = sf_let_star;
								goto start;
							}
							if (fname.IsSameAs(_T("load-time-value"), false))
							{
								eframe->isspecialform = true;
								eframe->specialform = sf_load_time_value;
								goto start;
							}
							if (fname.IsSameAs(_T("locally"), false))
							{
								eframe->isspecialform = true;
								eframe->specialform = sf_locally;
								goto start;
							}
							if (fname.IsSameAs(_T("macrolet"), false))
							{
								eframe->isspecialform = true;
								eframe->specialform = sf_macrolet;
								goto start;
							}
							if (fname.IsSameAs(_T("multiple-value-call"), false))
							{
								eframe->isspecialform = true;
								eframe->specialform = sf_multiple_value_call;
								goto start;
							}
							if (fname.IsSameAs(_T("multiple-value-prog1"), false))
							{
								eframe->isspecialform = true;
								eframe->specialform = sf_multiple_value_prog1;
								goto start;
							}
							if (fname.IsSameAs(_T("progn"), false))
							{
								eframe->isspecialform = true;
								eframe->specialform = sf_progn;
								goto start;
							}
							if (fname.IsSameAs(_T("progv"), false))
							{
								eframe->isspecialform = true;
								eframe->specialform = sf_progv;
								goto start;
							}
							if (fname.IsSameAs(_T("quote"), false))
							{
								eframe->isspecialform = true;
								eframe->specialform = sf_quote;
								goto start;
							}
							if (fname.IsSameAs(_T("return-from"), false))
							{
								eframe->isspecialform = true;
								eframe->specialform = sf_return_from;
								goto start;
							}
							if (fname.IsSameAs(_T("setq"), false))
							{
								eframe->isspecialform = true;
								eframe->specialform = sf_setq;
								goto start;
							}
							if (fname.IsSameAs(_T("symbol-macrolet"), false))
							{
								eframe->isspecialform = true;
								eframe->specialform = sf_symbol_macrolet;
								goto start;
							}
							if (fname.IsSameAs(_T("tagbody"), false))
							{
								eframe->isspecialform = true;
								eframe->specialform = sf_tagbody;
								goto start;
							}
							if (fname.IsSameAs(_T("the"), false))
							{
								eframe->isspecialform = true;
								eframe->specialform = sf_the;
								goto start;
							}
							if (fname.IsSameAs(_T("throw"), false))
							{
								eframe->isspecialform = true;
								eframe->specialform = sf_throw;
								goto start;
							}
							if (fname.IsSameAs(_T("unwind-protect"), false))
							{
								eframe->isspecialform = true;
								eframe->specialform = sf_unwind_protect;
								goto start;
							}
							// TODO: check if expr is a macro
							// not a special form or macro, then normal function call
							{
								wxMutexLocker(ngLispRuntime->globalmutex);
								eframe->mfunction = &(ngLispRuntime->globalenv.fmap[fname]);
								eframe->isfunction = true;
								goto start;
							}
						}
						else
						{
							wxLogError(_T("Expressions must start with a symbol"));
							goto pop;
						}
					}
					goto end;
				}
			case t_symbol:
				if (!(((symbol*)eframe->expr)->iskeyword))
				{
					wxMutexLocker(ngLispRuntime->globalmutex);
					eframe->result = ngLispRuntime->globalenv.vmap[((symbol*)eframe->expr)->name];
					if (!eframe->result)
					{
						wxLogError(_T("Variable %s has no value"), ((symbol*)eframe->expr)->name.c_str());
					}
					goto pop;
				}
			default:
				{
					// not a list, evals to itself
//					wxLogDebug(_T("Self evaling form"));
					eframe->result = eframe->expr;
					goto pop;
				}
			}
		}
		// expr is nil
		goto pop;
	}
	goto end;
	pop: // to pop an eval frame
	{
		evalframe* temp = eframe->parentframe;
		if (temp)
		{
//			wxLogDebug(_T("Going back to frame: %s"), (printstring(temp->expr)).c_str());
			if (temp->n > temp->i)
			{
				temp->eargs.Add(eframe->result);
//				if (eframe->nresults > 1)
//				{
//					// add extra results to temp frame
//				}
				temp->i++;
			}
		}
		if (eframe->deletable)
		{
			wxDELETE(eframe); // TODO: use a stack-frame pool ?
		}
		eframe = temp;
	}
	goto start;
	end:
	; // this ';' is to avoid a bug in gcc
}


value*
eval(value* expr, evalframe* env)
{
	if (expr)
	{
		switch (expr->getType())
		{
		// (consp expr)
		case t_cons:
			{
				value* fslot = car(expr);
				if (fslot)
				{
					if (fslot->getType() == t_symbol)
					{
						// special forms dispatch
						// TODO: case transformation of symbol name
						wxString fname = ((symbol*)fslot)->name;
						unsigned int params = fast_list_length((cons*)cdr(expr));
						if (fname.IsSameAs(_T("quote"), false))
						{
							if (params == 1)
							{
								return car(cdr(expr));
							}
							wxLogError(_T("Invalid number of arguments to special operator quote"));
							return NULL;
						}
						if (fname.IsSameAs(_T("setq"), false))
						{
							if (params % 2 == 0)
							{
								wxMutexLocker(ngLispRuntime->globalmutex);
								value* nparam = cdr(expr);
								value* result = NULL;
								for ( unsigned int i = 0; i < (params / 2) ; i++, nparam = cdr(cdr(nparam)) )
								{
									//eval the even args in the current lexenv
									if ((chk_nil(car(nparam)))->getType() != t_symbol)
									{
										wxLogError(_T("Argument %s for setq is not a symbol"), (printstring(car(nparam))).c_str());
										return NULL;
									}
									//TODO: lock common-lisp symbols
									symbol* sim = (symbol*)chk_nil(car(nparam));
									result = eval(car(cdr(nparam)), env);
									ngLispRuntime->globalenv.vmap[sim->name] = result;
								}
								return result;
							}
							else
							{
								wxLogError(_T("Odd number of arguments to special form setq"));
								return NULL;
							}
						}
						else // function or macro
						{
							if (env)
							{
								wxLogError(_T("We don't have lexical environments yet."));
							}
							else // global environment
							{
								internalfunction dafunc;
								// Accessing global memory, so we need to use a mutex
								{
									wxMutexLocker(ngLispRuntime->globalmutex);
									// TODO: check, we guess this uses a copy constructor but, huh?
									dafunc = ngLispRuntime->globalenv.fmap[fname];
								}
								switch (dafunc.ftype)
								{
								case f_invalid:
									wxLogError(_T("There is no function named %s"), fname.c_str());
									return NULL;
								case f_systemfunction:
									{
										// do full parameter parsing
										value* nparam = cdr(expr);
										cons* eparam = NULL;
										cons* mparam = NULL;
//										cons* kparam = NULL;
										unsigned int actualparams = 0;
										dafunc.paramvalues.Alloc(dafunc.numparams);
										if (dafunc.has_rest_args) dafunc.paramvalues.Add(NULL);
										//dafunc.paramvalues.Add(NULL);
										//dafunc.paramvalues[0] = NULL;
										unsigned int i;
										for (i = 0; nparam; actualparams++, i++)
										{
											if (!mparam)
											{
												mparam = new cons();
												eparam = mparam;
											}
											else
											{
												mparam->cdr = new cons();
												mparam = (cons*)(mparam->cdr);
											}
											if (nparam->getType() == t_cons)
											{
												mparam->car = eval(car(nparam), env);
												nparam = cdr(nparam);
											}
											else
											{
												wxLogError(_T("%s is not a list"), (printstring(nparam)).c_str());
												nparam = NULL;
											}
										}
										mparam = eparam;
										for (i = 0; mparam; actualparams++, i++, mparam = (cons*)cdr(mparam))
										{
											//eval the parameter in the current lexenv
											dafunc.paramvalues.Add(NULL);
											bool used;
											if (mparam && mparam->getType() == t_cons)
											{
												dafunc.paramvalues[i] = car(mparam);
												used = true;
											}
											else
											{
												//dafunc.paramvalues[i] = NULL;
												used = false;
											}
											if (i >= dafunc.numparams && !dafunc.has_rest_args)
											{
												wxLogError(_T("Too many parameters for function %s"), fname.c_str());
												return NULL;
											}
											if (i < dafunc.numparams)
											{
												switch (dafunc.paramspecifiers[i]->paramspec)
												{
												case p_required:
													if (used)
													{
														if (!chk_type(dafunc.paramvalues[i], dafunc.paramspecifiers[i]->ptype))
														{
															wxLogError(_T("Type error in parameter %d for function %s"), i+1, fname.c_str());
															return NULL;
														}
													}
													else
													{
														wxLogError(_T("Too few parameters for function %s"), fname.c_str());
														return NULL;
													}
													break;
												case p_optional:
													if (used)
													{
														if (dafunc.paramspecifiers[i+1]->paramspec == p_supplied)
															dafunc.paramvalues[i+1] = trueval();
													}
													else
													{
														dafunc.paramvalues[i] = eval(dafunc.paramspecifiers[i]->init_form, env);
														if (dafunc.paramspecifiers[i+1]->paramspec == p_supplied)
															dafunc.paramvalues[i+1] = NULL;
													}
													break;
												case p_supplied:
													// handled in the p_optional case
													break;
												case p_rest: case p_body:
													if (used)
													{
														dafunc.paramvalues[i] = mparam;
//														kparam = mparam;
													}
													else 
													{
														dafunc.paramvalues[i] = NULL;
													}
													break;
												case p_keyword:
													// TODO
													break;
												case p_symbol: //an alias for a keyword
													// TODO
													break;
												case p_aux:
													// TODO
													break;
												}
											}
										}
										if (actualparams < dafunc.reqparams) //dafunc.has_required_args && 
										{
											wxLogError(_T("Too few parameters for function %s"), fname.c_str());
											return NULL;
										}
										return dafunc.CallSystemFun();
										// TODO: this yield is necesary, or, even useful?
//										(wxThread::This())->Yield();
									}
								default:
									wxLogError(_T("Ups, we have to call %s"), fname.c_str());
									return NULL;
								}
							}
						}
					}
					else
					{
						wxLogError(_T("Expressions must start with a symbol"));
						return NULL;
					}
				}
				else
				{
					wxLogError(_T("Expressions must start with a valid symbol"));
					return NULL;
				}
			}
			break;
		// (not (consp expr))
		case t_symbol:
			if (((symbol*)expr)->iskeyword)
			{
				return expr;
			}
			// look up the value of the symbol in the environment.
			value* daval;
			{
				wxMutexLocker(ngLispRuntime->globalmutex);
				daval = ngLispRuntime->globalenv.vmap[((symbol*)expr)->name];
			}
			if (!daval)
			{
				wxLogError(_T("Variable %s has no value"), ((symbol*)expr)->name.c_str());
			}
			return daval;
			
		// other values evaluate to themselves.
		default:
			return expr;
		}
	}
	else
	{
		// NULL is nil, so it evaluates to itself
		return expr;
	}
	return NULL;
}


wxString printstring(value* expr)
{
	wxString output;
	wxStringOutputStream ostream(&output);
	print(expr, ostream);
	return output;
}


void 
repl::SignalError()
{
	wxMutexLocker lock(*m_debugmutex);
	m_debugcondition->Signal();
}


void*
repl::Entry()
{
//	wxString istring;
//	istring = this->GetValue();
	wxStringInputStream istream(*istring);
	wxString ostring;
//	wxStringOutputStream guiostream(&ostring);
	wxString printstr;
	value* readstuff;
	value* evalstuff;
	
	// do the stuff
	repl_reader.haschar = false;
	repl_reader.init();
	ostring.Empty();
	if (guioutput)
	{
		MutexGuiLocker mguilocker;
		response->SetDefaultStyle(delimitersstyle);
		printstr.Printf(_T("\n> "));
		response->AppendText(printstr);
		response->SetDefaultStyle(defaultstyle   );
//		printstr.Printf(_T("\n> %s"), istring->c_str());
		response->AppendText(*istring);
//		guiprint(readstuff, *response);
	}
//	else
//	{
//		ostream->Write(printstr.c_str(), printstr.Len());
//	}
	readstuff = repl_reader.read(istream);
	do
	{
//		print(eval(readstuff, NULL), guiostream);
//		printstr.Printf(_T("\n= %s"), ostring.c_str());
		if (guioutput)
		{
			MutexGuiLocker mguilocker;
			evalstuff = eval(readstuff, NULL);
			response->SetDefaultStyle(delimitersstyle);
			response->AppendText(_T("\n= "));
			response->SetDefaultStyle(defaultstyle   );
			guiprint(evalstuff, *response);
//			response->AppendText(printstr);
		}
		else
		{
			print(eval(readstuff, NULL), *ostream);
//			ostream->Write(printstr.c_str(), printstr.Len());
		}
		repl_reader.init();
		ostring.Empty();
		readstuff = repl_reader.read(istream);
	}
	while (istream.CanRead());//!( || readstuff)
	if (readstuff)
	{
//		print(eval(readstuff, NULL), guiostream);
//		printstr.Printf(_T("\n= %s"), ostring.c_str());
		if (guioutput)
		{
			MutexGuiLocker mguilocker;
			evalstuff = eval(readstuff, NULL);
			response->SetDefaultStyle(delimitersstyle);
			response->AppendText(_T("\n= "));
			response->SetDefaultStyle(defaultstyle   );
			guiprint(evalstuff, *response);
//			response->AppendText(printstr);
		}
		else
		{
			print(eval(readstuff, NULL), *ostream);
//			ostream->Write(printstr.c_str(), printstr.Len());
		}
	}
	if (guioutput)
	{
		// ir al final del texto 
		MutexGuiLocker mguilocker;
		response->SetInsertionPointEnd();
//		int sx, sy, scx, scy;
//		response->GetVirtualSize(&sx, &sy);
//		response->GetScrollPixelsPerUnit(&scx, &scy);
//		response->Scroll(-1, sy*(scy+1));
	}
	return NULL;
}


lispruntime* ngLispRuntime;


void lispruntime::init_common_lisp()
{
	globalenv.fmap[_T("type-of")].ftype = f_systemfunction;
	globalenv.fmap[_T("type-of")].paramspecifiers.Add(new parameter());
	globalenv.fmap[_T("type-of")].paramspecifiers[0]->paramspec = p_required;
	globalenv.fmap[_T("type-of")].paramspecifiers[0]->ptype = t_value;
	globalenv.fmap[_T("type-of")].has_required_args = true;
	globalenv.fmap[_T("type-of")].numparams = 1;
	globalenv.fmap[_T("type-of")].reqparams = 1;
	globalenv.fmap[_T("type-of")].funptr.f01 = type_of;
	
	globalenv.fmap[_T("car")].ftype = f_systemfunction;
	globalenv.fmap[_T("car")].paramspecifiers.Add(new parameter());
	globalenv.fmap[_T("car")].paramspecifiers[0]->paramspec = p_required;
	globalenv.fmap[_T("car")].paramspecifiers[0]->ptype = t_nilcons;
	globalenv.fmap[_T("car")].has_required_args = true;
	globalenv.fmap[_T("car")].numparams = 1;
	globalenv.fmap[_T("car")].reqparams = 1;
	globalenv.fmap[_T("car")].funptr.f01 = car;
	
	globalenv.fmap[_T("cdr")].ftype = f_systemfunction;
	globalenv.fmap[_T("cdr")].paramspecifiers.Add(new parameter());
	globalenv.fmap[_T("cdr")].paramspecifiers[0]->paramspec = p_required;
	globalenv.fmap[_T("cdr")].paramspecifiers[0]->ptype = t_nilcons;
	globalenv.fmap[_T("cdr")].has_required_args = true;
	globalenv.fmap[_T("cdr")].numparams = 1;
	globalenv.fmap[_T("cdr")].reqparams = 1;
	globalenv.fmap[_T("cdr")].funptr.f01 = cdr;
	
	globalenv.fmap[_T("first")].ftype = f_systemfunction;
	globalenv.fmap[_T("first")].paramspecifiers.Add(new parameter());
	globalenv.fmap[_T("first")].paramspecifiers[0]->paramspec = p_required;
	globalenv.fmap[_T("first")].paramspecifiers[0]->ptype = t_nilcons;
	globalenv.fmap[_T("first")].has_required_args = true;
	globalenv.fmap[_T("first")].numparams = 1;
	globalenv.fmap[_T("first")].reqparams = 1;
	globalenv.fmap[_T("first")].funptr.f01 = car;
	
	globalenv.fmap[_T("rest")].ftype = f_systemfunction;
	globalenv.fmap[_T("rest")].paramspecifiers.Add(new parameter());
	globalenv.fmap[_T("rest")].paramspecifiers[0]->paramspec = p_required;
	globalenv.fmap[_T("rest")].paramspecifiers[0]->ptype = t_nilcons;
	globalenv.fmap[_T("rest")].has_required_args = true;
	globalenv.fmap[_T("rest")].numparams = 1;
	globalenv.fmap[_T("rest")].reqparams = 1;
	globalenv.fmap[_T("rest")].funptr.f01 = cdr;
	
	globalenv.fmap[_T("cons")].ftype = f_systemfunction;
	globalenv.fmap[_T("cons")].paramspecifiers.Add(new parameter());
	globalenv.fmap[_T("cons")].paramspecifiers.Add(new parameter());
	globalenv.fmap[_T("cons")].paramspecifiers[0]->paramspec = p_required;
	globalenv.fmap[_T("cons")].paramspecifiers[0]->ptype = t_value;
	globalenv.fmap[_T("cons")].paramspecifiers[1]->paramspec = p_required;
	globalenv.fmap[_T("cons")].paramspecifiers[1]->ptype = t_value;
	globalenv.fmap[_T("cons")].has_required_args = true;
	globalenv.fmap[_T("cons")].numparams = 2;
	globalenv.fmap[_T("cons")].reqparams = 2;
	globalenv.fmap[_T("cons")].funptr.f02 = fcons;
	
	globalenv.fmap[_T("rplaca")].ftype = f_systemfunction;
	globalenv.fmap[_T("rplaca")].paramspecifiers.Add(new parameter());
	globalenv.fmap[_T("rplaca")].paramspecifiers.Add(new parameter());
	globalenv.fmap[_T("rplaca")].paramspecifiers[0]->paramspec = p_required;
	globalenv.fmap[_T("rplaca")].paramspecifiers[0]->ptype = t_cons;
	globalenv.fmap[_T("rplaca")].paramspecifiers[1]->paramspec = p_required;
	globalenv.fmap[_T("rplaca")].paramspecifiers[1]->ptype = t_value;
	globalenv.fmap[_T("rplaca")].has_required_args = true;
	globalenv.fmap[_T("rplaca")].numparams = 2;
	globalenv.fmap[_T("rplaca")].reqparams = 2;
	globalenv.fmap[_T("rplaca")].funptr.f02 = rplaca;
	
	globalenv.fmap[_T("rplacd")].ftype = f_systemfunction;
	globalenv.fmap[_T("rplacd")].paramspecifiers.Add(new parameter());
	globalenv.fmap[_T("rplacd")].paramspecifiers.Add(new parameter());
	globalenv.fmap[_T("rplacd")].paramspecifiers[0]->paramspec = p_required;
	globalenv.fmap[_T("rplacd")].paramspecifiers[0]->ptype = t_cons;
	globalenv.fmap[_T("rplacd")].paramspecifiers[1]->paramspec = p_required;
	globalenv.fmap[_T("rplacd")].paramspecifiers[1]->ptype = t_value;
	globalenv.fmap[_T("rplacd")].has_required_args = true;
	globalenv.fmap[_T("rplacd")].numparams = 2;
	globalenv.fmap[_T("rplacd")].reqparams = 2;
	globalenv.fmap[_T("rplacd")].funptr.f02 = rplacd;
	
	globalenv.fmap[_T("list")].ftype = f_systemfunction;
	globalenv.fmap[_T("list")].paramspecifiers.Add(new parameter());
	globalenv.fmap[_T("list")].paramspecifiers[0]->paramspec = p_rest;
	globalenv.fmap[_T("list")].paramspecifiers[0]->ptype = t_restarg;
	globalenv.fmap[_T("list")].has_rest_args = true;
	globalenv.fmap[_T("list")].numparams = 1;
	globalenv.fmap[_T("list")].reqparams = 0;
	globalenv.fmap[_T("list")].funptr.f01 = list;
	
	globalenv.fmap[_T("list-length")].ftype = f_systemfunction;
	globalenv.fmap[_T("list-length")].paramspecifiers.Add(new parameter());
	globalenv.fmap[_T("list-length")].paramspecifiers[0]->paramspec = p_required;
	globalenv.fmap[_T("list-length")].paramspecifiers[0]->ptype = t_nilcons;
	globalenv.fmap[_T("list-length")].numparams = 1;
	globalenv.fmap[_T("list-length")].reqparams = 1;
	globalenv.fmap[_T("list-length")].funptr.f01 = list_length;
	
	globalenv.fmap[_T("atom")].ftype = f_systemfunction;
	globalenv.fmap[_T("atom")].paramspecifiers.Add(new parameter());
	globalenv.fmap[_T("atom")].paramspecifiers[0]->paramspec = p_required;
	globalenv.fmap[_T("atom")].paramspecifiers[0]->ptype = t_value;
	globalenv.fmap[_T("atom")].numparams = 1;
	globalenv.fmap[_T("atom")].reqparams = 1;
	globalenv.fmap[_T("atom")].funptr.f01 = atom;
	
	globalenv.fmap[_T("consp")].ftype = f_systemfunction;
	globalenv.fmap[_T("consp")].paramspecifiers.Add(new parameter());
	globalenv.fmap[_T("consp")].paramspecifiers[0]->paramspec = p_required;
	globalenv.fmap[_T("consp")].paramspecifiers[0]->ptype = t_value;
	globalenv.fmap[_T("consp")].numparams = 1;
	globalenv.fmap[_T("consp")].reqparams = 1;
	globalenv.fmap[_T("consp")].funptr.f01 = consp;
	
	globalenv.fmap[_T("not")].ftype = f_systemfunction;
	globalenv.fmap[_T("not")].paramspecifiers.Add(new parameter());
	globalenv.fmap[_T("not")].paramspecifiers[0]->paramspec = p_required;
	globalenv.fmap[_T("not")].paramspecifiers[0]->ptype = t_value;
	globalenv.fmap[_T("not")].numparams = 1;
	globalenv.fmap[_T("not")].reqparams = 1;
	globalenv.fmap[_T("not")].funptr.f01 = not;
	
	globalenv.fmap[_T("+")].ftype = f_systemfunction;
	globalenv.fmap[_T("+")].paramspecifiers.Add(new parameter());
	globalenv.fmap[_T("+")].paramspecifiers[0]->paramspec = p_rest;
	globalenv.fmap[_T("+")].paramspecifiers[0]->ptype = t_restarg;
	globalenv.fmap[_T("+")].has_rest_args = true;
	globalenv.fmap[_T("+")].numparams = 1;
	globalenv.fmap[_T("+")].funptr.f01 = sum;
	
	globalenv.fmap[_T("-")].ftype = f_systemfunction;
	globalenv.fmap[_T("-")].paramspecifiers.Add(new parameter());
	globalenv.fmap[_T("-")].paramspecifiers.Add(new parameter());
	globalenv.fmap[_T("-")].paramspecifiers[0]->paramspec = p_required;
	globalenv.fmap[_T("-")].paramspecifiers[0]->ptype = t_number;
	globalenv.fmap[_T("-")].paramspecifiers[1]->paramspec = p_rest;
	globalenv.fmap[_T("-")].paramspecifiers[1]->ptype = t_restarg;
	globalenv.fmap[_T("-")].has_required_args = true;
	globalenv.fmap[_T("-")].has_rest_args = true;
	globalenv.fmap[_T("-")].reqparams = 1;
	globalenv.fmap[_T("-")].numparams = 2;
	globalenv.fmap[_T("-")].funptr.f02 = sus;
	
	globalenv.fmap[_T("*")].ftype = f_systemfunction;
	globalenv.fmap[_T("*")].paramspecifiers.Add(new parameter());
	globalenv.fmap[_T("*")].paramspecifiers[0]->paramspec = p_rest;
	globalenv.fmap[_T("*")].paramspecifiers[0]->ptype = t_restarg;
	globalenv.fmap[_T("*")].has_rest_args = true;
	globalenv.fmap[_T("*")].numparams = 1;
	globalenv.fmap[_T("*")].funptr.f01 = mul;
	
	globalenv.fmap[_T("/")].ftype = f_systemfunction;
	globalenv.fmap[_T("/")].paramspecifiers.Add(new parameter());
	globalenv.fmap[_T("/")].paramspecifiers.Add(new parameter());
	globalenv.fmap[_T("/")].paramspecifiers[0]->paramspec = p_required;
	globalenv.fmap[_T("/")].paramspecifiers[0]->ptype = t_number;
	globalenv.fmap[_T("/")].paramspecifiers[1]->paramspec = p_rest;
	globalenv.fmap[_T("/")].paramspecifiers[1]->ptype = t_restarg;
	globalenv.fmap[_T("/")].has_required_args = true;
	globalenv.fmap[_T("/")].has_rest_args = true;
	globalenv.fmap[_T("/")].numparams = 2;
	globalenv.fmap[_T("/")].reqparams = 1;
	globalenv.fmap[_T("/")].funptr.f02 = fdiv;
	
	globalenv.fmap[_T("!")].ftype = f_systemfunction;
	globalenv.fmap[_T("!")].paramspecifiers.Add(new parameter());
	globalenv.fmap[_T("!")].paramspecifiers[0]->paramspec = p_required;
	globalenv.fmap[_T("!")].paramspecifiers[0]->ptype = t_integer;
	globalenv.fmap[_T("!")].has_required_args = true;
	globalenv.fmap[_T("!")].numparams = 1;
	globalenv.fmap[_T("!")].reqparams = 1;
	globalenv.fmap[_T("!")].funptr.f01 = factorial;
	
	globalenv.fmap[_T("eval")].ftype = f_systemfunction;
	globalenv.fmap[_T("eval")].paramspecifiers.Add(new parameter());
	globalenv.fmap[_T("eval")].paramspecifiers[0]->paramspec = p_required;
	globalenv.fmap[_T("eval")].paramspecifiers[0]->ptype = t_value;
	globalenv.fmap[_T("eval")].has_required_args = true;
	globalenv.fmap[_T("eval")].numparams = 1;
	globalenv.fmap[_T("eval")].reqparams = 1;
	globalenv.fmap[_T("eval")].funptr.f01 = feval;
	
	globalenv.vmap[_T("nil")] = NULL;
	globalenv.vmap[_T("t")] = new symbol(_T("t"));
}
